home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-06-01 | 6.6 KB | 165 lines | [TEXT/CCL ] |
- ; Ted Kaehler and Dave Patterson a taste of SmallTalk
- ; W. W. Norton ed., chapter 6, pp. 83 ff.
- ; translated in Allegro Common Lisp by Jean-Pascal J. LANGE.
- ; © Copyright 1988 Jean-Pascal J. LANGE.
-
- (proclaim '(optimize (speed 3)
- (space 0)
- (safety 0)
- (compilation-speed 0) ))
-
- (defStruct (towerByRules (:include animatedTowerOfHanoi))
- #| An object of this class represents the game. It holds an array
- of stacks that hold disks. It also keeps track of which disk
- just moved and which disk should move next.
- The new instance variables are
- oldDisk the disk that was moved last time,
- currentDisk we are considering moving this disk,
- destinationDisk and putting it on top of this disk.|#
- (oldDisk nil)
- (currentDisk nil)
- (destinationDisk nil) )
-
- ; initialize
-
- (deFun HanoiRules (thisTower)
- ; asks the user how many disks, set up the game and move disks until
- ; we are done
- (declare (special *TheTowers* *Thickness* *DiskGap*))
- (do ()
- ((integerp (howMany thisTower)))
- (format t "~&Please type the number of disks in the tower: ")
- (setf (towerByRules-howMany thisTower) (read)) )
- (oneOf *window*
- :window-title "heuristic animated towers of Hanoï"
- :window-position #@(20 100)
- :window-size #@(360 220)
- :window-type :single-edge-box )
-
- (setUpDisksRules thisTower) ; create the disks and stacks
-
- (loop ; iterate until all disks are on one tower again.
- (let* ((currentDisk (decide thisTower))
- ; decide which to move and also set destinationDisk
- (currentPole (pole currentDisk))
- (destinationPole
- (pole (towerByRules-destinationDisk thisTower)) ) )
- (removeFirst (towerByRules-stacks thisTower)
- (1- currentPole) )
- (addFirst (towerByRules-stacks thisTower)
- (1- destinationPole) currentDisk )
- #|(format t "~&~D -> ~D : ~A"
- currentPole destinationPole (name currentDisk) )|#
- ; tell the disk where it is now
- (moveUponRules currentDisk (towerByRules-destinationDisk thisTower))
- ; get ready for the next move
- (setf (towerByRules-oldDisk thisTower) currentDisk) )
- (when (allOnOneTower thisTower) (return)) ) ; test if done
- ; so on next run, howMany will be re-initialized
- (setf (towerByRules-howMany thisTower) nil)
- (makUnbound '*TheTowers*)
- (makUnbound '*Thickness*)
- (makUnbound '*DiskGap*)
- nil ) ; HanoiRules
-
- (deFun setUpDisksRules (thisTower)
- ; Creates the disks and set up the poles. Tells all disks what game
- ; they are in and set disk thickness and gap.
- (whichTowers thisTower)
- (let ((displayBox
- (originCorner #@(0 0) (ask (front-window) (window-size))) ))
- (erase displayBox)
- (border displayBox 2) )
- ; the poles are an array of three stacks. Each stack is a list.
- (setf (towerByRules-stacks thisTower)
- (make-array 3 :initial-element nil) )
- (let ((disk)
- (size (howMany thisTower)) )
- (doTimes (i (howMany thisTower))
- (setq disk (make-HanoiDiskRules)) ; create a disk
- (widthPoleRules disk size 1)
- ; don't forget: the first element of an array is at index 0 !!!
- ; push it onto a stack
- (addFirst (towerByRules-stacks thisTower) 0 disk)
- (invert disk) ; show on the screen
- (setq size (1- size)) ) )
-
- ; When a pole has no disk on it, one of these mock disks acts as a
- ; bottom disk. A moving disk will ask a mock disk its width and pole number.
- (setf (towerByRules-mockDisks thisTower)
- (make-array 3 :initial-element nil) )
- (let ((disk))
- (doTimes (index 3)
- (setq disk (make-HanoiDiskRules))
- ; don't forget: a doTimes-loop index starts at 0 !!!
- (widthPoleRules disk 1000 (1+ index))
- (setf (aRef (towerByRules-mockDisks thisTower) index)
- disk ) ) )
- ; on the first move, look for another disk (a real one) to move
- ; don't forget: the first element of an array is at index 0 !!!
- (setf (towerByRules-oldDisk thisTower)
- (aRef (towerByRules-mockDisks thisTower) 2)) )
- ; setUpDisksRules
-
- ; moves
-
- (deFun allOnOneTower (thisTower)
- ; return true if all of the disks are on one tower
- (doTimes (index (length (towerByRules-stacks thisTower)) nil)
- (if (= (length (aRef (towerByRules-stacks thisTower) index))
- (howMany thisTower) )
- (return t) ) ) ) ; allOnOneTower
-
- (deFun decide (thisTower)
- ; use the last disk moved (oldDisk) to find a new disk to move
- ; (currentDisk) and a disk to put it on top of (destinationDisk).
- (topsOtherThan
- thisTower
- (towerByRules-oldDisk thisTower)
- #'(lambda (movingDisk)
- (cond ((hasLegalMove movingDisk)
- ; remember the disk upon which to move
- (setf (towerByRules-destinationDisk thisTower)
- (bestMove movingDisk) )
- ; return the disk that moves
- movingDisk ) ) ) ) ) ; decide
-
- (deFun polesOtherThan (thisTower thisDisk aBlock)
- ; evaluate the block of code using the top disk on each of the other
- ; two poles. If a pole is empty, use the mock disk for that pole.
- (doTimes (aPole 3)
- ; Want a pole other than the pole of thisDisk
- ; don't forget: a doTimes-loop index starts at 0 !!!
- (if (not (= (1+ aPole) (pole thisDisk)))
- (let
- ((result
- (if (null (aRef (towerByRules-stacks thisTower) aPole))
- ; if the pole is empty, use a mock disk…
- (funCall aBlock
- (aRef (towerByRules-mockDisks thisTower)
- aPole ) ) ; execute the block
- ; …else use the top disk
- (funCall aBlock ; execute the block
- (first (aRef (towerByRules-stacks thisTower)
- aPole )) ) ) ))
- (when result (return result)) ) ) ) ) ; polesOtherThan
-
- (deFun topsOtherThan (thisTower thisDisk aBlock)
- ; evaluate the block of code using the top disk on each of the other
- ; two poles. If a pole is empty, ignore it. This is for actual disks.
- (doTimes (aPole 3)
- ; If the pole does not have thisDisk and is not empty, then
- ; execute aBlock (don't forget: a doTimes-loop index starts at 0)
- (if (and (not (= (1+ aPole) (pole thisDisk)))
- (not (null (aRef (towerByRules-stacks thisTower)
- aPole ))) )
- (let ((result
- (funcall aBlock ; execute the block
- (first (aRef (towerByRules-stacks thisTower)
- aPole )) ) ))
- (when result (return result)) ) ) ) ) ; topsOtherThan
-
- (deFun removeFirst (array index)
- ; removeFirst is the procedure for pop.
- (setf (aRef array index) (cdr (aRef array index))) ) ; removeFirst
-